package com.googlecode.mailnet.netmodel;

import java.util.Arrays;
import java.io.Serializable;

/**
 * Queueing configuration for a network. 
 *
 * @author Iskander Akishev
 */
public class QueueingConfiguration implements Serializable {

	// todo: write proper documentation

	private final NetworkConfiguration networkConfiguration;

	/**
	 * Total number of priority gradations in the network; the value is positive.
	 * <p/>
	 * The priority gradations are numbered from <tt>0</tt> to <tt>gradationsNumber - 1</tt>;
	 * lesser values correspiond to higher priorities.
	 */
	private int queuesNumber;

	/**
	 * Size limits for queues of each priority, measured in packages.
	 * <p/>
	 * The size of array is equal to {@link #queuesNumber}.
	 */
	private int[] queueLimits;

	/**
	 * Weight coefficients for different priority gradations.
	 * todo: TBD
	 * <p/>
	 * The size of array is equal to {@link #queuesNumber}.
	 */
	private int[] queueWeights;

	/**
	 * todo: TBD
	 * <p/>
	 * The size of array is equal to <tt>{@link #queuesNumber} - 1</tt>.
	 */
	private boolean[] isStrictlyPrioritized;

	private static final int DEFAULT_LIMIT = 1000000;
	private static final int DEFAULT_WEIGHT = 1;

	private static final int DEFAULT_QUEUES_NUMBER = 1;

	QueueingConfiguration(NetworkConfiguration networkConfiguration) {
		assert networkConfiguration != null;
		this.networkConfiguration = networkConfiguration;
		setQueuesNumber(DEFAULT_QUEUES_NUMBER);
	}

	public int getQueuesNumber() {
		return queuesNumber;
	}

	public void setQueuesNumber(int queuesNumber) {
		if (queuesNumber < 1) {
			throw new IllegalArgumentException();
		}
		networkConfiguration.checkBeforeModifications();

		if (queuesNumber == this.queuesNumber) {
			return;
		}
		int[] newLimits = new int[queuesNumber];
		int[] newWeights = new int[queuesNumber];
		boolean[] newStrict = new boolean[queuesNumber - 1];
		if (queuesNumber > this.queuesNumber) {
			// extend
			Arrays.fill(newLimits, DEFAULT_LIMIT);
			Arrays.fill(newWeights, DEFAULT_WEIGHT);
			if (this.queuesNumber != 0) {
				// not inside the constructor
				System.arraycopy(queueLimits, 0, newLimits, 0, this.queuesNumber);
				System.arraycopy(queueWeights, 0, newWeights, 0, this.queuesNumber);
				System.arraycopy(isStrictlyPrioritized, 0, newStrict, 0, this.queuesNumber - 1);
				if (isStrictlyPrioritized.length > 0) {
					// extending the last value
					Arrays.fill(newStrict, this.queuesNumber, queuesNumber,
						isStrictlyPrioritized[this.queuesNumber - 1]);
				}
			}
		} else {
			// shrink
			System.arraycopy(queueLimits, 0, newLimits, 0, queuesNumber);
			System.arraycopy(queueWeights, 0, newWeights, 0, queuesNumber);
			System.arraycopy(isStrictlyPrioritized, 0, newStrict, 0, queuesNumber - 1);
			// todo: validate priorities in all MessageFlow's
		}
		
		queueLimits = newLimits;
		queueWeights = newWeights;
		isStrictlyPrioritized = newStrict;
		this.queuesNumber = queuesNumber;
	}

	public int getQueueLimit(int i) {
		return queueLimits[i];
	}

	public int getQueueWeight(int i) {
		return queueWeights[i];
	}

	public boolean isStrictlyPrioritized(int i) {
		return isStrictlyPrioritized[i];
	}

	public void setQueueLimit(int i, int limit) {
		networkConfiguration.checkBeforeModifications();
		if (limit < 1) {
			throw new IllegalArgumentException("limit must be positive");
		}
		if (i < 0 || i >= queueLimits.length) return;
		queueLimits[i] = limit;
	}

	public void setQueueWeight(int i, int weight) {
		networkConfiguration.checkBeforeModifications();
		if (weight < 1) {
			throw new IllegalArgumentException("weight must be positive");
		}
		if (i < 0 || i >= queueWeights.length) return;
		queueWeights[i] = weight;
	}

	public void setStrictlyPrioritized(int i, boolean value) {
		networkConfiguration.checkBeforeModifications();
		if (i < 0 || i >= isStrictlyPrioritized.length) return;
		isStrictlyPrioritized[i] = value;
	}
}
